package top.blackr.folivora.config;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 拦截配置——数据权限（列过滤）
 * @author BlackR
 */
//@Component todo 下划线转驼峰，映射会失效
@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class DataScopeColumnInterceptor implements Interceptor {

    private static final String MAPPED_STATEMENT = "mappedStatement";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        List<Object> resList = new ArrayList<>();
        DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(defaultResultSetHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue(MAPPED_STATEMENT);
        // 获取节点属性的集合
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        if (resultMapCount > 0) {
            Statement statement = (Statement) invocation.getArgs()[0];
            ResultSet resultSet = statement.getResultSet();
            if (resultSet != null) {
                // Oracle适配:oracleResultSetMetaData.isSearchable(1);

                // 获取mybatis返回的实体类类型名
                Class<?> resultType = resultMaps.get(0).getType();
                // 获得对应列名
                ResultSetMetaData rsmd = resultSet.getMetaData();
                List<ResultMetaData> resultMetaDatas = this.getResultMetaData(rsmd);

                while (resultSet.next()) {
                    Map<String, Object> resultMap = new LinkedHashMap<>();
                    for (ResultMetaData resultMetaData : resultMetaDatas) {
                        // 字段根据权限控制过滤，不需要过滤的才调用put，从redis中获取用户权限信息，解析表——字段
                        resultMap.put(resultMetaData.getColumnName(), resultSet.getString(resultMetaData.getColName()));
                    }
                    Object bean = resultType.getDeclaredConstructor().newInstance();
                    // 将转换后的map转换为实体类中
                    Map<String, Object> beanToMap = BeanUtil.beanToMap(bean);
                    resultMap.putAll(beanToMap);
                    resList.add(resultMap);
                }
                return resList;
            }
        }
        return invocation.proceed();
    }

    /**
     * 主要是为了把这个拦截器生成一个代理放到拦截器链中
     * Description包装目标对象 为目标对象创建代理对象
     *
     * @param target 为要拦截的对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    private List<ResultMetaData> getResultMetaData(ResultSetMetaData resultSetMetaData) throws SQLException {
        List<ResultMetaData> resultMetaData = new ArrayList<>();
        for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
            resultMetaData.add(new ResultMetaData(
                    // 实体类类型
                    resultSetMetaData.getColumnClassName(i + 1),
                    // 数据库名
                    resultSetMetaData.getCatalogName(i + 1),
                    // 结果集名
                    resultSetMetaData.getSchemaName(i + 1),
                    // 字段别名（as 后面那个）
                    resultSetMetaData.getColumnLabel(i + 1),
                    // 字段名
                    resultSetMetaData.getColumnName(i + 1),
                    // 表名
                    resultSetMetaData.getTableName(i + 1),
                    // 字段类型
                    resultSetMetaData.getColumnTypeName(i + 1)));
        }
        return resultMetaData;
    }

    @Data
    @AllArgsConstructor
    static class ResultMetaData {
        private String columnClassName;
        private String catalogName;
        private String schemaName;
        private String columnLabel;
        private String columnName;
        private String tableName;
        private String columnTypeName;

        /**
         * columnLabel the label for the column specified with the SQL AS clause.<br>
         * If the SQL AS clause was not specified, then the label is the name of the column
         * @return ColName
         */
        public String getColName() {
            if (StrUtil.equals(this.columnName,this.columnLabel)) {
                return this.columnName;
            }
            return this.columnLabel;
        }
    }


}
